package vn.edu.uit.wifi_transfering;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import protocol.AcceptSendFile;
import protocol.ControlMessage;
import protocol.ErrorMessage;
import protocol.FileInfo;
import protocol.LoginMessage;
import protocol.Message;
import protocol.SuccessMessage;
import protocol.TransferMessage;

public class ClientThread implements Runnable {

	private String username = "lexuanvu";
	private String password = "qwerty";
	private boolean isAnonymous = false;

	private Socket socket;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	private Thread thread;

	public ClientThread(Socket sk) {
		System.out.println("Client is comming...");
		this.socket = sk;
	}

	public void start() {
		try {
			oos = new ObjectOutputStream(socket.getOutputStream());
			ois = new ObjectInputStream(socket.getInputStream());
			thread = new Thread(this);
			thread.start();
			while (!thread.isAlive()) {
				//
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	public void stop() throws IOException {

		if (oos != null) {
			oos.close();
		}

		if (ois != null) {
			ois.close();
		}

		if (socket != null) {
			socket.shutdownInput();
			socket.shutdownOutput();
			socket.close();
		}		
	}

	public void run() {
		// TODO Auto-generated method stub
		try {
			// if (!authentication((Message) ois.readObject())) {
			// return;
			// }

			while (true) {
				dispatchMessage((Message) ois.readObject());
				
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private int checkAccount(LoginMessage loginMsg) {
		String user = (String) loginMsg.username;
		String pass = (String) loginMsg.password;
		boolean isAnonymous = loginMsg.isAnonymous;

		if (this.isAnonymous && !isAnonymous) {
			return 1;
		} else if (!this.isAnonymous && isAnonymous) {
			return 2;
		} else if (!this.isAnonymous && !isAnonymous) {
			this.password = md5(this.password);
			if (this.username.equals(user) && this.password.equals(pass)) {
				return 0;
			} else {
				return 3;
			}
		} else {
			return 0;
		}
	}

	void authentication(Message msg) throws IOException {
		if (msg instanceof LoginMessage) {

			int check = checkAccount((LoginMessage) msg);

			switch (check) {
			case 1:
				oos.writeObject(new ErrorMessage(1));
				break;
			case 2:
				oos.writeObject(new ErrorMessage(2));
				break;
			case 3:
				oos.writeObject(new ErrorMessage(3));
				break;
			default:
				oos.writeObject(new SuccessMessage());
				break;
			}
			
			stop();
		}

	}

	private void dispatchMessage(Message msg) throws IOException {
		if (msg instanceof ControlMessage) {
			if (msg instanceof LoginMessage) {
				authentication(msg);				
			}
		} else if (msg instanceof TransferMessage) {
			if (msg instanceof FileInfo) {
				String checksum = ((FileInfo) msg).fileChecksum;
				String fileName = ((FileInfo) msg).filename;
				System.out.println("File name is : " + fileName);
				oos.writeObject(new AcceptSendFile());		
				fSendFile(checksum, fileName);
			}
		}
	}
	
	private void fSendFile(String crc, String fileName) throws IOException {	
		
		int fileSize = 10*1024*1024;
		int bytesRead;
		int current = 0;
		
		byte[] myByteArray = new byte[fileSize];
		InputStream is = socket.getInputStream();	
		
		bytesRead = is.read(myByteArray, 0, myByteArray.length);
		current = bytesRead;
		
		do {
			bytesRead = is.read(myByteArray, current, myByteArray.length - current);				
			if (bytesRead >= 0) {
				current += bytesRead;
			}
		} while(bytesRead > -1);			
		
		FileOutputStream fos = new FileOutputStream(fileName);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		bos.write(myByteArray, 0, current);			
		bos.flush();
		bos.close();
		socket.shutdownOutput();
		
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			FileInputStream fis = new FileInputStream(fileName);
			
			byte[] dataBytes = new byte[1024];
			int nread = 0;
			while((nread = fis.read(dataBytes)) != -1) {
				md5.update(dataBytes, 0, nread);				
			}
			
			//md5.update(pass.getBytes("UTF-8"));
			
			StringBuffer result = new StringBuffer();
			for(byte b : md5.digest()) {
				result.append(Integer.toHexString((b & 0xf0) >>> 4));				
				result.append(Integer.toHexString(b & 0x0f));
			}
			
			System.out.println(result.toString());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String md5(String string) {
		if (string == null || string.trim().length() < 1) {
			return null;
		}
		try {
			return getMD5(string.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	private String getMD5(byte[] source) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			StringBuffer result = new StringBuffer();
			// lay tung 4 bit nhi phan sau do cong voi 1111 roi chuyen sang hexa
			for (byte b : md5.digest(source)) {
				result.append(Integer.toHexString((b & 0xf0) >>> 4)); // lay 4
																		// bit
																		// dau
																		// chuyen
																		// sang
																		// he
																		// hex
				result.append(Integer.toHexString(b & 0x0f));// lay 4 bit sau
																// chuyen sang
																// he hex
			}
			return result.toString();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

}
